package org.xxpay.pay.channel.saaspay;

import com.alipay.api.internal.util.codec.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.xxpay.core.common.util.MD5Util;
import org.xxpay.core.common.util.MyLog;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * 
 *<p>工具类</p>
 * @author  NOT-FIND
 * @version 2018年7月19日 NOT-FIND
 */
public class SaaspayUtil {
	private static final MyLog _log = MyLog.getLog(SaaspayUtil.class);

	/**
	 * H5PAY配置
	 */
	public static class H5PayBody{
		//消息ID
		String msgId;
		//报文请求时间，格式yyyy-MM-ddHH:mm:ss
		String requestTimestamp;
		//商户订单号
		String merOrderId;
		//请求系统预留字段
		String srcReserve;
		//商户号
		String mid;
		//终端号
		String tid;
		//业务类型
		String instMid;
		//商品信息
		List<GoodsItem> goods;
		//商户附加数据
		String attachedData;
		//账单描述
		String orderDesc;
		//商品标记
		String goodsTag;
		//订单原始金额
		String originalAmount;
		//支付总金额
		String totalAmount;
		//订单过期时间
		String expireTime;
		//担保交易标识
		String secureTransaction;
		//支付结果通知地址
		String notifyUrl;
		//网页跳转地址
		String returnUrl;
		//系统ID
		String systemId;
		//业务应用类型
		String sceneType;
		//应用名称
		String merAppName;
		//应用标识
		String merAppId;
		//是否需要限制信用卡支付
		String limitCreditCard;
		//分账标记
		String divisionFlag;
		//平台商户分账金额
		String platformAmount;
		//子订单信息
		List<SubOrderItem> subOrders;
		//实名认证姓名
		String name;
		//实名认证手机号
		String mobile;
		//实名认证证件类型
		String certType;
		//实名认证证件号
		String certNo;
		//卡号
		String bankCardNo;

		String toJson(){
			StringBuilder sb = new StringBuilder();
			sb.append("{");
			if (this.msgId != null) sb.append("\"msgId\":\"" + this.msgId + "\",");
			if (this.requestTimestamp != null) sb.append("\"requestTimestamp\":\"" + this.requestTimestamp + "\",");
			if (this.merOrderId != null) sb.append("\"merOrderId\":\"" + this.merOrderId + "\",");
			if (this.srcReserve != null) sb.append("\"srcReserve\":\"" + this.srcReserve + "\",");
			if (this.mid != null) sb.append("\"mid\":\"" + this.mid + "\",");
			if (this.tid != null) sb.append("\"tid\":\"" + this.tid + "\",");
			if (this.instMid != null) sb.append("\"instMid\":\"" + this.instMid + "\",");
			if (this.goods != null && this.goods.size()>0) {
				sb.append("\"goods\":[");
				for(int i=0;i<goods.size();i++){
					sb.append(goods.get(i));
					sb.append(",");
				}
				if (sb.charAt(sb.length() - 1) == ',')
					sb.deleteCharAt(sb.length() - 1);
				sb.append("],");
			}
			if (this.attachedData != null) sb.append("\"attachedData\":\"" + this.attachedData + "\",");
			if (this.orderDesc != null) sb.append("\"orderDesc\":\"" + this.orderDesc + "\",");
			if (this.goodsTag != null) sb.append("\"goodsTag\":\"" + this.goodsTag + "\",");
			if (this.originalAmount != null) sb.append("\"originalAmount\":\"" + this.originalAmount + "\",");
			if (this.totalAmount != null) sb.append("\"totalAmount\":\"" + this.totalAmount + "\",");
			if (this.expireTime != null) sb.append("\"expireTime\":\"" + this.expireTime + "\",");
			if (this.secureTransaction != null) sb.append("\"secureTransaction\":\"" + this.secureTransaction + "\",");
			if (this.notifyUrl != null) sb.append("\"notifyUrl\":\"" + this.notifyUrl + "\",");
			if (this.returnUrl != null) sb.append("\"returnUrl\":\"" + this.returnUrl + "\",");
			if (this.systemId != null) sb.append("\"systemId\":\"" + this.systemId + "\",");
			if (this.sceneType != null) sb.append("\"sceneType\":\"" + this.sceneType + "\",");
			if (this.merAppName != null) sb.append("\"merAppName\":\"" + this.merAppName + "\",");
			if (this.merAppId != null) sb.append("\"merAppId\":\"" + this.merAppId + "\",");
			if (this.limitCreditCard != null) sb.append("\"limitCreditCard\":\"" + this.limitCreditCard + "\",");
			if (this.divisionFlag != null) sb.append("\"divisionFlag\":\"" + this.divisionFlag + "\",");
			if (this.platformAmount != null) sb.append("\"platformAmount\":\"" + this.platformAmount + "\",");
			if (this.subOrders != null && this.subOrders.size()>0) {
				sb.append("\"subOrders\":[");
				for(int i=0;i<subOrders.size();i++){
					sb.append(subOrders.get(i));
					sb.append(",");
				}
				if (sb.charAt(sb.length() - 1) == ',')
					sb.deleteCharAt(sb.length() - 1);
				sb.append("],");
			}
			if (this.name != null) sb.append("\"name\":\"" + this.name + "\",");
			if (this.mobile != null) sb.append("\"mobile\":\"" + this.mobile + "\",");
			if (this.certType != null) sb.append("\"certType\":\"" + this.certType + "\",");
			if (this.certNo != null) sb.append("\"certNo\":\"" + this.certNo + "\",");
			if (this.bankCardNo != null) sb.append("\"bankCardNo\":\"" + this.bankCardNo + "\",");
			if (sb.charAt(sb.length() - 1) == ',')
				sb.deleteCharAt(sb.length() - 1);
			sb.append("}");
			return sb.toString();
		}

		public String toString(){
			return this.toJson();
		}
		public static class GoodsItem {
			//商品ID
			String goodsId;
			//商品名称
			String goodsName;
			//商品数量
			String quantity;
			//商品单价（分）
			String price;
			//商品分类
			String goodsCategory;
			//商品说明
			String body;
			//子商户号
			String subMerchantId;
			//子商户商品总额
			int subOrderAmount;

			String toJson(){
				StringBuilder sb = new StringBuilder();
				sb.append("{");
				if(this.goodsId!=null) sb.append("\"goodsId\":\""+this.goodsId+"\",");
				if(this.goodsName!=null) sb.append("\"goodsName\":\""+this.goodsName+"\",");

				if(this.quantity!=null){
					sb.append("\"quantity\":\""+this.quantity+"\",");
				}
				if(this.price!=null){
					sb.append("\"price\":\""+this.price+"\",");
				}
				if(this.goodsCategory!=null){
					sb.append("\"goodsCategory\":\""+this.goodsCategory+"\",");
				}
				if(this.body!=null){
					sb.append("\"body\":\""+this.body+"\",");
				}
				if(this.subMerchantId!=null){
					sb.append("\"subMerchantId\":\""+this.subMerchantId+"\",");
				}
				if(this.subOrderAmount!=0){
					sb.append("\"subOrderAmount\":\""+this.subOrderAmount+"\",");
				}
				if(sb.charAt(sb.length()-1) == ',')
					sb.deleteCharAt(sb.length()-1);
				sb.append("}");
				return sb.toString();
			}
			public String toString(){
				return this.toJson();
			}
		}
		public static class SubOrderItem{
			//子商户号
			String mid;
			//子商户分账金额
			int totalAmount;
			String toJson() {
				StringBuilder sb = new StringBuilder();
				sb.append("{");
				if (this.mid != null) {
					sb.append("\"mid\":\"" + this.mid + "\",");
				}
				if (this.totalAmount != 0) {
					sb.append("\"totalAmount\":\"" + this.totalAmount + "\",");
				}
				if (sb.charAt(sb.length() - 1) == ',')
					sb.deleteCharAt(sb.length() - 1);
				sb.append("}");
				return sb.toString();
			}

			public String toString(){
				return this.toJson();
			}
		}

	}


	/**
	 * 发送httpGET请求
	 * @param url 请求url
	 * @return response
	 */
	public static String h5Request(String url){
		StringBuffer sbf = new StringBuffer();
		String response = "";
		PrintWriter out = null;
		BufferedReader in = null;
		try{
			URL realUrl = new URL(url);
			URLConnection conn = realUrl.openConnection();
			HttpURLConnection httpUrlConnection = (HttpURLConnection) conn;
			httpUrlConnection.setRequestMethod("GET");
			httpUrlConnection.setReadTimeout(15000);
			httpUrlConnection.setReadTimeout(60000);
			httpUrlConnection.connect();
			in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(),"UTF-8"));
			String line;
			while ((line = in.readLine()) != null) {
				sbf.append(line);
				sbf.append("\r\n");
			}
			response = sbf.toString();
		}catch(Exception e){
			e.printStackTrace();
		} finally {
			try {
				if (out != null) { out.close();}
				if (in != null) {in.close();}
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return response;
	}


	/**
	 * 获取签名头
	 * @param appid
	 * @param appkey
	 * @param timestamp 格式:"yyyyMMddHHmmss"
	 * @param nonce 随机字符串，
	 * @param body 请求体
	 * @return authorization 认证报文
	 * @throws Exception
	 */
	public static String getSignature(String appid, String appkey, String timestamp, String nonce, String body) throws Exception {
		byte[] data = body.getBytes("utf-8");
		InputStream is = new ByteArrayInputStream(data);
		String testSH = DigestUtils.sha256Hex(is);
		String s1 = appid+timestamp+nonce+testSH;
		_log.info("待签名串：\n{}，\n签名密钥：\n{}", s1, appkey);
		Mac mac = Mac.getInstance("HmacSHA256");
		mac.init(new SecretKeySpec(appkey.getBytes("utf-8"),"HmacSHA256"));
		byte[] localSignature = mac.doFinal(s1.getBytes("utf-8"));
		String localSignatureStr = Base64.encodeBase64String(localSignature);
		_log.info("待签结果：\n{}", localSignatureStr);
		return localSignatureStr;
	}

	public static String generateSHA256Signature(String data) {
		StringBuilder hexString = new StringBuilder();
		try {
			MessageDigest digest = MessageDigest.getInstance("SHA-256");
			_log.info("待签名串：{}",data);
			byte[] hash = digest.digest(data.getBytes());
			for (byte b : hash) {
				String hex = Integer.toHexString(0xff & b);
				if (hex.length() == 1) {
					hexString.append('0');
				}
				hexString.append(hex);
			}
		}catch (Exception e){
			_log.error("签名错误：{},{}",e.getMessage(),e.getCause());
		}
		return hexString.toString();
	}

	/**
	 * 查询参数
	 */
	public static class QueryBody{
		//消息ID
		String msgId;
		//报文请求时间，格式yyyy-MM-ddHH:mm:ss
		String requestTimestamp;
		//请求系统预留字段
		String srcReserve;
		//商户号
		String mid;
		//终端号
		String tid;
		//业务类型
		String instMid;
		//商户订单号
		String merOrderId;

		String toJson(){
			StringBuilder sb = new StringBuilder();
			sb.append("{");
			if (this.msgId != null) sb.append("\"msgId\":\"" + this.msgId + "\",");
			if (this.requestTimestamp != null) sb.append("\"requestTimestamp\":\"" + this.requestTimestamp + "\",");
			if (this.srcReserve != null) sb.append("\"srcReserve\":\"" + this.srcReserve + "\",");
			if (this.mid != null) sb.append("\"mid\":\"" + this.mid + "\",");
			if (this.tid != null) sb.append("\"tid\":\"" + this.tid + "\",");
			if (this.instMid != null) sb.append("\"instMid\":\"" + this.instMid + "\",");
			if (this.merOrderId != null) sb.append("\"merOrderId\":\"" + this.merOrderId + "\",");
			if (sb.charAt(sb.length() - 1) == ',')
				sb.deleteCharAt(sb.length() - 1);
			sb.append("}");
			return sb.toString();
		}
		public String toString(){
			return this.toJson();
		}
	}

	/**
	 * 查询部分获取签名头
	 * @param appid
	 * @param appkey
	 * @param timestamp 格式:"yyyyMMddHHmmss"
	 * @param nonce 随机字符串，
	 * @param body 请求体
	 * @return authorization 认证报文
	 * @throws Exception
	 */
	static String getAuthorization(String appid, String appkey, String timestamp, String nonce, String body){
		String localSignatureStr = "";
		try {
			byte[] data = body.getBytes("utf-8");
			InputStream is = new ByteArrayInputStream(data);
			String testSH = DigestUtils.sha256Hex(is);
			String s1 = appid+timestamp+nonce+testSH;
			_log.info("查询待签名串：\n{}，\n签名密钥：\n{}", s1, appkey);
			Mac mac = Mac.getInstance("HmacSHA256");
			mac.init(new SecretKeySpec(appkey.getBytes("utf-8"),"HmacSHA256"));
			byte[] localSignature = mac.doFinal(s1.getBytes("utf-8"));
			localSignatureStr = Base64.encodeBase64String(localSignature);
			_log.info("签名结果：\n{}", localSignatureStr);
		}catch (Exception e){
			_log.error("签名错误：{},{}",e.getMessage(),e.getCause());
		}
		return  "OPEN-BODY-SIG AppId="+"\""+appid+"\""+", Timestamp="+"\""+timestamp+"\""+", Nonce="+"\""+nonce+"\""+", Signature="+"\""+localSignatureStr+"\"";
	}


	/**
	 * 查询发送http请求
	 * @param url 请求url
	 * @param authorization 认证报文
	 * @param reqBody  请求体
	 * @return response
	 */
	static String queryRequest(String url, String authorization, String reqBody){
		String response = "";
		PrintWriter out = null;
		BufferedReader in = null;
		try{
			URL realUrl = new URL(url);
			URLConnection conn = realUrl.openConnection();
			HttpURLConnection httpUrlConnection = (HttpURLConnection) conn;
			httpUrlConnection.setRequestProperty("Content-Type", "application/json");
			httpUrlConnection.setRequestProperty("authorization",authorization);
			httpUrlConnection.setDoOutput(true);
			httpUrlConnection.setDoInput(true);
			out = new PrintWriter(httpUrlConnection.getOutputStream());
			out.write(reqBody);
			out.flush();
			httpUrlConnection.connect();
			in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				response += line;
			}
		}catch(Exception e){
			e.printStackTrace();
		} finally {
			try {
				if (out != null) { out.close();}
				if (in != null) {in.close();}
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return response;
	}


	public static class H5PayRefundBody{
		//消息ID
		String msgId;
		//报文请求时间，格式yyyy-MM-ddHH:mm:ss
		String requestTimestamp;
		//请求系统预留字段
		String srcReserve;
		//商户订单号
		String merOrderId;
		//业务类型
		String instMid;
		//商户号
		String mid;
		//终端号
		String tid;
		//要退货的金额
		String refundAmount;
		//退款订单号
		String refundOrderId;
		//平台商户退款分账金额
		String platformAmount;
		//子订单信息
		List<SubOrderItem> subOrders;
		//退款说明
		String refundDesc;

		String toJson(){
			StringBuilder sb = new StringBuilder();
			sb.append("{");
			if (this.msgId != null) sb.append("\"msgId\":\"" + this.msgId + "\",");
			if (this.requestTimestamp != null) sb.append("\"requestTimestamp\":\"" + this.requestTimestamp + "\",");
			if (this.srcReserve != null) sb.append("\"srcReserve\":\"" + this.srcReserve + "\",");
			if (this.merOrderId != null) sb.append("\"merOrderId\":\"" + this.merOrderId + "\",");
			if (this.instMid != null) sb.append("\"instMid\":\"" + this.instMid + "\",");
			if (this.mid != null) sb.append("\"mid\":\"" + this.mid + "\",");
			if (this.tid != null) sb.append("\"tid\":\"" + this.tid + "\",");
			if (this.refundAmount != null) sb.append("\"refundAmount\":\"" + this.refundAmount + "\",");
			if (this.refundOrderId != null) sb.append("\"refundOrderId\":\"" + this.refundOrderId + "\",");
			if (this.platformAmount != null) sb.append("\"platformAmount\":\"" + this.platformAmount + "\",");
			if (this.subOrders != null && this.subOrders.size()>0) {
				sb.append("\"subOrders\":[");
				for(int i=0;i<subOrders.size();i++){
					sb.append(subOrders.get(i));
					sb.append(",");
				}
				if (sb.charAt(sb.length() - 1) == ',')
					sb.deleteCharAt(sb.length() - 1);
				sb.append("],");
			}
			if (this.refundDesc != null) sb.append("\"refundDesc\":\"" + this.refundDesc + "\",");
			if (sb.charAt(sb.length() - 1) == ',')
				sb.deleteCharAt(sb.length() - 1);
			sb.append("}");
			return sb.toString();
		}
		public String toString(){
			return this.toJson();
		}
		static class SubOrderItem{
			//子商户号
			String mid;
			//子商户分账金额
			int totalAmount;
			String toJson() {
				StringBuilder sb = new StringBuilder();
				sb.append("{");
				if (this.mid != null) {
					sb.append("\"mid\":\"" + this.mid + "\",");
				}
				if (this.totalAmount != 0) {
					sb.append("\"totalAmount\":\"" + this.totalAmount + "\",");
				}
				if (sb.charAt(sb.length() - 1) == ',')
					sb.deleteCharAt(sb.length() - 1);
				sb.append("}");
				return sb.toString();
			}
			public String toString(){
				return this.toJson();
			}
		}

	}

	/**
	 * 查询发送post请求
	 * @param url 请求url
	 * @param authorization 认证报文
	 * @param reqBody  请求体
	 * @return response
	 */
	static String refundRequest(String url, String authorization, String reqBody){
		String response = "";
		PrintWriter out = null;
		BufferedReader in = null;
		try{
			URL realUrl = new URL(url);
			URLConnection conn = realUrl.openConnection();
			HttpURLConnection httpUrlConnection = (HttpURLConnection) conn;
			httpUrlConnection.setRequestProperty("Content-Type", "application/json");
			httpUrlConnection.setRequestProperty("authorization",authorization);
			httpUrlConnection.setDoOutput(true);
			httpUrlConnection.setDoInput(true);
			out = new PrintWriter(httpUrlConnection.getOutputStream());
			out.write(reqBody);
			out.flush();
			httpUrlConnection.connect();
			in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream()));
			String line;
			while ((line = in.readLine()) != null) {
				response += line;
			}
		}catch(Exception e){
			e.printStackTrace();
		} finally {
			try {
				if (out != null) { out.close();}
				if (in != null) {in.close();}
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		return response;
	}
	
}
